package com.md.model.word;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.md.model.base.FileStream;
import com.md.model.base.StyleGroup;
import com.md.model.base.UploadClient;
import com.md.model.html.LabelStyle;
import com.md.util.ObjectUtil;
import com.md.util.POIUtil;
import com.md.value.html.ColorIndex;
import com.md.value.html.Font;
import com.md.value.html.LabelStyleType;
import com.md.value.html.Position;
import lombok.Getter;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFPictureData;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
import org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFontsList;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHeight;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPrGeneral;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTShd;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyle;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyles;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPrBase;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTextDirection;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTrPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.FontsDocument;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STHeightRule;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STStyleType;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTextDirection;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @author 刘骄阳
 * 2024-06-14 15:39
 */
@Getter
public class Document {
  private final ThemeDocumentStyle themeDocument;
  private final FontDocumentStyle fontDocument;
  private final SettingsDocumentStyle settingsDocument;
  private final Map<String, List<String>> fontsAltName;
  private final Map<String, CTStyle> styleMap;
  private final Map<STStyleType.Enum, CTStyle> defaultStyleMap;
  private final Map<String, StyleGroup> customStyle;
  private final CTStyles styles;
  private final XWPFDocument xwpfDocument;
  private final UploadClient uploadClient;

  public Document(final XWPFDocument document, final UploadClient uploadClient)
    throws XmlException, IOException {
    this.uploadClient = uploadClient;
    xwpfDocument = document;
    styles = document.getStyle();
    themeDocument = new ThemeDocumentStyle(document);
    fontDocument = new FontDocumentStyle(document);
    settingsDocument = new SettingsDocumentStyle(document);
    fontsAltName = Maps.newHashMap();
    styleMap = Maps.newHashMap();
    defaultStyleMap = Maps.newHashMap();
    customStyle = Maps.newHashMap();
    initFontMaps(fontDocument.getFontsDocuments());
    initialize();
  }

  public StyleGroup getCustomStyle(final String styleId) {
    if (ObjectUtil.isEmpty(styleId)) {
      return new StyleGroup("X");
    }
    return customStyle.getOrDefault(styleId, new StyleGroup(styleId));
  }

  private void initFontMaps(final List<FontsDocument> fontsDocuments) {
    fontsDocuments.stream().map(FontsDocument::getFonts).map(CTFontsList::getFontList).forEach(ctFonts ->
      ctFonts.forEach(font -> {
        String[] fonts = Optional.ofNullable(font.getAltName()).filter(altName ->
            ObjectUtil.notEmpty(altName.getVal())).map(altName -> altName.getVal().split(","))
          .orElse(new String[0]);

        List<String> fontNames = Lists.newArrayList();
        for (final String fontName : fonts) {
          if (ObjectUtil.notEmpty(fontName)) {
            fontNames.add(fontName);
          }
        }
        fontsAltName.put(font.getName(), fontNames);
      }));
  }


  private void initialize() {
    initStyle();
  }

  private void initStyle() {
    styles.getStyleList().forEach(style -> {
      boolean isDefaultStyle = POIUtil.isON(style.xgetDefault());
      if (isDefaultStyle) {
        Optional.ofNullable(style.getType()).ifPresent(type -> defaultStyleMap.put(type, style));
      }
      StyleGroup styleGroup = customInitStyle(style);
      customStyle.put(style.getStyleId(), styleGroup);
      // todo 此处是否应该不记录默认样式?
      styleMap.put(style.getStyleId(), style);
    });
  }


  private StyleGroup customInitStyle(final CTStyle style) {
    String styleId = style.getStyleId();
    if (ObjectUtil.notEmpty(styleId)) {
      StyleGroup styleGroup = new StyleGroup(getClassName(styleId));

      // 对齐方式、背景色
      getPositionStyle(style.getPPr(), styleGroup);
      getFontStyle(style.getRPr(), styleGroup);
      getTableWeight(style.getTblPr(), styleGroup);
      getTrHeight(style.getTrPr(), styleGroup);
      getStyle(style.getTcPr(), styleGroup);
      return styleGroup;
    }
    return null;
  }

  private String getClassName(final String styleId) {
    StringBuilder className = new StringBuilder();
    char firstChar = styleId.charAt(0);
    if (Character.isDigit(firstChar)) {
      // 类名不能以数字开头(Chrome不适用)
      className.append('X');
    }
    return className.append(styleId).toString();
  }

  private void getPositionStyle(final CTPPrGeneral pPr, final StyleGroup styleGroup) {
    if (pPr != null) {
      Position position = new Position(pPr);
      position.getLabelStyle().forEach(styleGroup::addStyle);
    }
  }


  private void getFontStyle(final CTRPr rPr, final StyleGroup styleGroup) {
    if (rPr != null) {
      Font font = new Font(rPr, themeDocument.getThemeDocuments());
      font.getLabelStyle().forEach(styleGroup::addStyle);
    }
  }

  private void getTableWeight(final CTTblPrBase tblPr, final StyleGroup styleGroup) {
    if (tblPr != null) {
      POIUtil.getWeight(tblPr.getTblW()).ifPresent(styleGroup::addStyle);
    }
  }

  private void getTrHeight(final CTTrPr trPr, final StyleGroup styleGroup) {
    Optional.ofNullable(trPr).map(CTTrPr::getTrHeightArray).ifPresent(ctHeights -> {
      if (ObjectUtil.isEmpty(ctHeights)) {
        return;
      }
      CTHeight trHeight = ctHeights[0];
      STHeightRule.Enum hRule = trHeight.getHRule();
      if (hRule != null) {
        Float height = POIUtil.getFloatValue(trHeight.xgetVal());
        switch (hRule.intValue()) {
          case STHeightRule.INT_AT_LEAST:
            styleGroup.addStyle(LabelStyle.of(LabelStyleType.MIN_HEIGHT, height));

            break;
          case STHeightRule.INT_EXACT:
            styleGroup.addStyle(LabelStyle.of(LabelStyleType.HEIGHT, height));
            break;
          default:
            break;
        }
      }
    });
  }

  private void getStyle(final CTTcPr tcPr, final StyleGroup styleGroup) {
    if (tcPr != null) {
      // 宽
      POIUtil.getWeight(tcPr.getTcW()).ifPresent(styleGroup::addStyle);

      // 背景色
      Optional.ofNullable(tcPr.getShd()).map(CTShd::xgetColor).map(stHexColor ->
        POIUtil.getColor(stHexColor, ColorIndex.WHITE.getHex())).ifPresent(color ->
        styleGroup.addStyle(LabelStyle.of(LabelStyleType.BACKGROUND_COLOR, "#" + color)));

      // 旋转方式
      CTTextDirection direction = tcPr.getTextDirection();
      if (direction != null) {
        // see http://www.css3maker.com/text-rotation.html
        switch (direction.getVal().intValue()) {
          case STTextDirection.INT_BT_LR:
            String r270 = "rotate(270deg)";
            styleGroup.addStyle(LabelStyle.of(LabelStyleType.TRANSFORM_WEBKIT_TRANSFORM, r270));
            styleGroup.addStyle(LabelStyle.of(LabelStyleType.TRANSFORM_MOZ_TRANSFORM, r270));
            styleGroup.addStyle(LabelStyle.of(LabelStyleType.TRANSFORM_O_TRANSFORM, r270));
            styleGroup.addStyle(LabelStyle.of(LabelStyleType.TRANSFORM_WRITING_MODE, "bt-lr"));
            break;
          case STTextDirection.INT_TB_RL:
            String r90 = "rotate(90deg)";
            styleGroup.addStyle(LabelStyle.of(LabelStyleType.TRANSFORM_WEBKIT_TRANSFORM, r90));
            styleGroup.addStyle(LabelStyle.of(LabelStyleType.TRANSFORM_MOZ_TRANSFORM, r90));
            styleGroup.addStyle(LabelStyle.of(LabelStyleType.TRANSFORM_O_TRANSFORM, r90));
            styleGroup.addStyle(LabelStyle.of(LabelStyleType.TRANSFORM_WRITING_MODE, "tb-rl"));
            break;
          default:
            break;
        }
      }
    }
  }

  public Optional<XWPFPictureData> getPictureData(final CTPicture picture) {
    return Optional.ofNullable(picture).map(CTPicture::getBlipFill).map(CTBlipFillProperties::getBlip)
      .map(CTBlip::getEmbed).map(xwpfDocument::getPictureDataByID);
  }

  public String uploadFile(final FileStream fileStream) {
    return Optional.ofNullable(uploadClient).map(temp -> temp.uploadFile(fileStream)).orElse(null);
  }
}
